package com.harmonyos.samples.utils;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.harmonyos.samples.action.DemoTestAction;
import com.harmonyos.samples.bean.code.CodeInfo;
import com.harmonyos.samples.bean.code.LogInfo;
import com.harmonyos.samples.bean.repository.RepositoriesInfo;
import com.harmonyos.samples.constants.Constants;
import com.harmonyos.samples.listener.GitListener;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationAction;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.codeStyle.CodeStyleManager;
import git4idea.commands.GitCommand;
import git4idea.commands.GitCommandResult;
import git4idea.commands.GitImpl;
import git4idea.commands.GitLineHandler;
import org.apache.commons.io.FileUtils;
import org.jetbrains.annotations.NotNull;

import java.awt.*;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class InsertUtils {

    /**
     * 打开文件选择器
     *
     * @param project
     * @return
     */
    public static String openFileChooser(Project project, String rootPath) {
//        FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false) {
//            @Override
//            public boolean isFileSelectable(@Nullable VirtualFile file) {
//                String currentFilePath = file.getCanonicalPath();
//                if (currentFilePath != null) {
//                    int index = currentFilePath.indexOf("src");
//                    if (index != -1) {
//                        currentFilePath = currentFilePath.substring(0, index);
//                    }
//                }
//                return file.getPath().startsWith(currentFilePath);
//            }
//        };
        FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false);
        VirtualFile root = VirtualFileManager.getInstance().findFileByNioPath(new File(rootPath).toPath());
        descriptor.withTitle("选择文件").withDescription("请选择需要添加代码的Ability文件路径")
                .withShowHiddenFiles(true).withTreeRootVisible(true).withRoots(root);
        VirtualFile[] directories = FileChooser.chooseFiles(descriptor, project, root);
        if (directories.length > 0) {
            return directories[0].getCanonicalPath();
        }
        return null;
    }

    /**
     * 通过git下载case仓案例源代码
     *
     * @param anActionEvent
     * @param downloadPath  下载代码的地址
     * @param gitListener
     */
    public static void downloadCode(@NotNull AnActionEvent anActionEvent, String rootPath, String downloadPath, CodeInfo codeInfo, GitListener gitListener) {

        ApplicationManager.getApplication().executeOnPooledThread(() -> {

            ProgressManager.getInstance().run(new Task.Backgroundable(anActionEvent.getProject(), "Downloading code") {

                @Override
                public void run(@NotNull ProgressIndicator progressIndicator) {
                    progressIndicator.setText("Prepare...");
                    progressIndicator.setIndeterminate(true);

                    Project project = anActionEvent.getProject();
                    String casePath = codeInfo.getCasePath();
                    String caseRepositories = codeInfo.getCaseRepositoriesInfo();
                    notification(project, "Start download, please wait for a moment...", false, null);

                    String[] temp = downloadPath.split("/");
                    String tempPath = getCacheDirectory().getAbsolutePath() + "/" + temp[temp.length - 1];
                    File tempFile = new File(tempPath);
                    if (!tempFile.exists()) {
                        tempFile.mkdirs();
                    }
                    VirtualFile dir = LocalFileSystem.getInstance().findFileByPath(tempPath);
                    if (dir == null) {
                        if (gitListener != null) {
                            gitListener.onGitResult("Directory not found");
                        }
                        return;
                    }
                    GitImpl gitImpl = new GitImpl();

                    boolean result = executeGit(gitImpl, project, dir, GitCommand.INIT, gitListener);
                    if (!result) {
                        return;
                    }

                    result = executeGit(gitImpl, project, dir, GitCommand.CONFIG, gitListener, "core.sparsecheckout", "true");
                    if (!result) {
                        return;
                    }

                    File sparseCheckoutFile = new File(tempPath, ".git/info/sparse-checkout");
                    try (FileWriter writer = new FileWriter(sparseCheckoutFile)) {
                        writer.write(casePath + "\n");
                    } catch (IOException e) {
                        if (gitListener != null) {
                            gitListener.onGitResult("Write file failed");
                        }
                    }

                    if (needAddRemote(gitImpl, project, dir, caseRepositories)) {
                        result = executeGit(gitImpl, project, dir, GitCommand.REMOTE, gitListener, "add", "origin", caseRepositories);
                        if (!result) {
                            return;
                        }
                    }

                    progressIndicator.setText("Pull code from gitee, Please wait...");
                    result = executeGit(gitImpl, project, dir, GitCommand.PULL, gitListener, "origin", "master", "--rebase");
                    if (result) {
                        tempPath = tempPath + "/" + casePath.replaceAll("\\\\", "/");
                        String finalTempPath = tempPath;
                        ApplicationManager.getApplication().invokeLater(() -> {
                            try {
                                copyDirectory(project, finalTempPath, downloadPath);
                            } catch (IOException ignored) {
                                ignored.printStackTrace();
                            }
                            VirtualFileManager.getInstance().refreshWithoutFileWatcher(false);

                            deleteCode(anActionEvent, downloadPath);
                            VirtualFileManager.getInstance().refreshWithoutFileWatcher(false);
                            notification(project, "Download succeed !", true, downloadPath.substring(0, downloadPath.lastIndexOf("/")));
                            progressIndicator.setText("Add code...");
                            insertCodeAtCaret(anActionEvent, codeInfo.getCodeAnnotation() + "\n" + codeInfo.getInsertCode());
                            insertImportCode(anActionEvent, codeInfo.getImportCode());
                            String currentPath = anActionEvent.getData(CommonDataKeys.VIRTUAL_FILE).getCanonicalPath();
                            int index = currentPath.indexOf("src");
                            if (index != -1) {
                                currentPath = currentPath.substring(0, index);
                            }
                            String[] temp1 = currentPath.split("/");
                            String[] temp2 = downloadPath.split("/");
                            String relativePath = "";
                            int min = Math.min(temp1.length, temp2.length);
                            int resultTemp = -1;
                            for (int i = 0; i < min; i++) {
                                if (!temp1[i].equals(temp2[i])) {
                                    resultTemp = i;
                                }
                            }
                            for (int i = resultTemp; i < temp2.length; i++) {
                                relativePath = relativePath + temp2[i] + "/";
                            }
                            relativePath = relativePath.substring(0, relativePath.lastIndexOf("/"));
                            writeDependenciesCode(anActionEvent, temp[temp.length - 1], "file:" + "../" + relativePath, "");
                            writeBuildProfile(anActionEvent, rootPath, temp[temp.length - 1], "./" + relativePath, codeInfo.isNapi(), "");
                            if (DemoTestAction.progressWindow != null) {
                                DemoTestAction.progressWindow.dispose();
                                DemoTestAction.progressWindow.stop();
                            }
                        });

                    }
                }
            });

        });

    }

    public static void writePermission(AnActionEvent anActionEvent, String filePath, List<String> permissions, RepositoriesInfo repositoriesInfo) {
        WriteCommandAction.runWriteCommandAction(anActionEvent.getProject(), () -> {
            VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(filePath);
            if (virtualFile != null) {
                Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
                if (document != null) {
                    String content = Json5Parser.parseJson5(document.getText());
                    JsonObject contentObject = JsonParser.parseString(content).getAsJsonObject();
                    if (contentObject.has("module")) {
                        JsonObject moduleObject = contentObject.getAsJsonObject("module");
                        if (moduleObject.has("requestPermissions")) {
                            JsonArray permissionArray = moduleObject.get("requestPermissions").getAsJsonArray();
                            for(String permission : permissions) {
                                boolean hasContainPermission = false;
                                for(int i = 0; i < permissionArray.size(); i++) {
                                    if (permissionArray.get(i).getAsJsonObject().get("name").getAsString().equals(permission)) {
                                        hasContainPermission = true;
                                        break;
                                    }
                                }
                                if (!hasContainPermission) {
                                    JsonObject jsonElement = new JsonObject();
                                    jsonElement.addProperty("name", permission);
                                    jsonElement.addProperty("reason", "$string:app_name");
                                    jsonElement.add("usedScene", new JsonObject());
                                    permissionArray.add(jsonElement);
                                }
                            }
                        } else {
                            JsonArray permissionArray = new JsonArray();
                            for(String permission : permissions) {
                                JsonObject jsonElement = new JsonObject();
                                jsonElement.addProperty("name", permission);
                                permissionArray.add(jsonElement);
                            }
                            moduleObject.add("requestPermissions", permissionArray);
                        }
                    }
                    document.replaceString(0, document.getTextLength(), contentObject.toString());
                    reformatCode(document, anActionEvent.getProject(), virtualFile);
                    StringBuilder temp = new StringBuilder();
                    for (String permission : permissions) {
                        temp.append(permission).append(" ");
                    }
                    updateLog(repositoriesInfo.getName(), "module.json5", "", "写入权限：" + temp.toString());
                }
            }
        });
    }

    public static void deleteCode(AnActionEvent anActionEvent, String path) {
        File file = new File(path + "/src/main/ets/FeatureComponent.ets");
        file.delete();
    }

    public static void updateLog(String libraryName, String fileName, String methodName, String code) {
        LogInfo logInfo = new LogInfo();
        logInfo.setFileName(fileName);
        logInfo.setMethodName(methodName);
        logInfo.setCode(code);
        logInfo.setLibraryName(libraryName);

    }

    private static String readFile(Project project, String filePath) {
        VirtualFile file = LocalFileSystem.getInstance().findFileByPath(filePath);
        if (file != null) {
            Document document = FileDocumentManager.getInstance().getDocument(file);
            if (document != null) {
                String result = document.getText();
                return result;
            }
        }
        return null;
    }

    private static void readAndWriteFile(AnActionEvent anActionEvent, String filePath, String... replaces) {

        VirtualFile file = LocalFileSystem.getInstance().findFileByPath(filePath);
        if (file != null) {
            Document document = FileDocumentManager.getInstance().getDocument(file);
            if (document != null) {
                String content = document.getText();
                if (replaces != null) {
                    for (String replace : replaces) {
                        content = content.replace(replace, "");
                    }
                }
                try {
                    String finalContent = content;
                    WriteCommandAction.runWriteCommandAction(anActionEvent.getProject(), () -> {
                        document.replaceString(0, document.getTextLength(), finalContent);
                        commitDocumentChange(document, anActionEvent.getProject());
                        reformatCode(document, Objects.requireNonNull(anActionEvent.getProject()), anActionEvent.getData(CommonDataKeys.VIRTUAL_FILE));
                    });

                } catch (RuntimeException ignored) {

                }

            }
        }

    }

    /**
     * 弹出通知
     *
     * @param project
     * @param message      通知信息
     * @param needOpenFile 是否需要打开文件
     * @param openFilePath 打开文件的路径（needOpenFile是false时，不需要传）
     */
    public static void notification(Project project, String message, boolean needOpenFile, String openFilePath) {
        Notification notification = new Notification(Constants.PLUGIN_GROUP_ID, Constants.PLUGIN_NOTIFICATION_TIP, "", NotificationType.INFORMATION);
        notification.setContent(message);
        if (needOpenFile && openFilePath != null && !openFilePath.isEmpty()) {
            notification.addAction(new NotificationAction(Constants.PLUGIN_NOTIFICATION_OPEN) {
                @Override
                public void actionPerformed(@NotNull AnActionEvent anActionEvent, @NotNull Notification notification) {
                    VirtualFile file = Objects.requireNonNull(LocalFileSystem.getInstance().findFileByPath(openFilePath));

                    if (file.isDirectory()) {
                        try {
                            Desktop.getDesktop().open(new File(openFilePath));
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    } else {
                        FileEditorManager.getInstance(project).openFile(file, true);
                    }

                }
            });
        }
        Notifications.Bus.notify(notification, project);
    }

    /**
     * git配置中远程仓库是否存在
     *
     * @param gitImpl
     * @param project
     * @param dir
     * @param remote  远程仓库地址
     * @return true-已存在远程仓，false-不存在远程仓
     */
    private static boolean needAddRemote(GitImpl gitImpl, Project project, VirtualFile dir, String remote) {
        GitLineHandler handler = new GitLineHandler(project, dir, GitCommand.REMOTE);
        handler.addParameters("-v");
        GitCommandResult gitResult = gitImpl.runCommand(handler);
        List<String> remoteList = gitResult.getOutput();
        if (remoteList.isEmpty()) {
            return true;
        }
        for (String s : remoteList) {
            if (s.equals(remote)) {
                return false;
            }
        }
        handler = new GitLineHandler(project, dir, GitCommand.REMOTE);
        handler.addParameters("rm", "origin");
        gitImpl.runCommand(handler);
        return true;
    }

    /**
     * 执行git命令
     *
     * @param gitImpl
     * @param project
     * @param dir
     * @param gitCommand  git命令
     * @param gitListener
     * @param params      git命令的参数
     * @return
     */
    private static boolean executeGit(GitImpl gitImpl, Project project, VirtualFile dir, GitCommand gitCommand, GitListener gitListener, String... params) {
        GitLineHandler handler = new GitLineHandler(project, dir, gitCommand);
        if (params != null && params.length > 0) {
            handler.addParameters(params);
        }

        GitCommandResult gitResult = gitImpl.runCommand(handler);

        int code = gitResult.getExitCode();
        if (code != 0) {
            if (gitListener != null) {
                gitListener.onGitResult(gitResult.getErrorOutput().get(0));
            }
            return false;
        }
        return true;
    }

    /**
     * 在光标处插入代码
     *
     * @param e    AnActionEvent对象
     * @param code 需要插入的代码块
     */
    public static void insertCodeAtCaret(@NotNull AnActionEvent e, String code) {
        if (code == null || code.isEmpty()) {
            return;
        }
        // 获取编辑器
        var editor = e.getData(CommonDataKeys.EDITOR);
        if (editor != null) {
            WriteCommandAction.runWriteCommandAction(editor.getProject(), () -> {
                var document = editor.getDocument();
                var caretModel = editor.getCaretModel();
                // 获取光标位置
                int caretOffset = caretModel.getOffset();
                // 在光标处插入代码
                document.insertString(caretOffset, code + "\n");
                reformatCode(document, Objects.requireNonNull(e.getProject()), e.getData(CommonDataKeys.VIRTUAL_FILE));
            });
        }
    }

    /**
     * 在光标处插入代码
     *
     */
    public static void insertCodeAtCaret(Project project, RepositoriesInfo repositoriesInfo, VirtualFile atFilePath, int caretPosition) {
        CodeInfo codeInfo = repositoriesInfo.getCodeInfo();
        if (codeInfo == null) {
            return;
        }
        if (atFilePath != null) {
            Document document = FileDocumentManager.getInstance().getDocument(atFilePath);
            if (document != null) {
                //在光标处插入组件代码
                document.insertString(DemoTestAction.rangeMarker.getStartOffset(), codeInfo.getCodeAnnotation() + "\n" + codeInfo.getInsertCode() + "\n");
                if (codeInfo.getInsertCode() != null && !codeInfo.getInsertCode().isEmpty()) {
                    updateLog(repositoriesInfo.getName(), atFilePath.getName(), "", codeInfo.getInsertCode());
                }
                //格式化代码
                reformatCode(document, Objects.requireNonNull(project), atFilePath);
                //判断文件是否已包含import代码
                String fileContent = document.getText();
                if (!fileContent.contains(codeInfo.getImportCode())) {
                    String regex = "import\\s+\\{\\s*([a-zA-Z0-9_$\\s,]+)\\s*\\}\\s+from\\s+['\"]([a-zA-Z0-9_\\-\\/]+)['\"];";
                    Pattern pattern = Pattern.compile(regex);
                    Matcher matcher = pattern.matcher(fileContent);
                    int lastIndex = 0;
                    while (matcher.find()) {
                        lastIndex = matcher.end();
                    }
                    String importCode = codeInfo.getImportCode();
                    if (lastIndex != 0) {
                        importCode = "\n" + importCode;
                    }
                    if (lastIndex == 0) {
                        if (document.getText().startsWith("/*") || document.getText().startsWith("/**")) {
                            lastIndex = document.getText().indexOf("*/") + 2;
                            importCode = "\n" + importCode;
                        }
                    }
                    document.insertString(lastIndex, importCode);
                    updateLog(repositoriesInfo.getName(), atFilePath.getName(), "", codeInfo.getImportCode());
                    reformatCode(document, Objects.requireNonNull(project), atFilePath);
                }
            }
        }
    }

    /**
     * 在当前文件中插入import代码
     *
     * @param e    AnActionEvent对象
     * @param code 需要插入的代码
     */
    public static void insertImportCode(@NotNull AnActionEvent e, String code) {
        if (code == null || code.isEmpty()) {
            return;
        }
        ApplicationManager.getApplication().invokeLater(() -> {
            VirtualFile file = e.getData(CommonDataKeys.VIRTUAL_FILE);
            if (file != null) {
                PsiFile psiFile = PsiManager.getInstance(Objects.requireNonNull(e.getProject())).findFile(file);
                if (psiFile != null) {
                    Document document = FileDocumentManager.getInstance().getDocument(file);
                    if (document != null) {
                        WriteCommandAction.runWriteCommandAction(e.getProject(), () -> {
                            String fileContent = document.getText();
                            if (fileContent.contains(code)) {
                                return;
                            }
                            String regex = "import\\s+\\{\\s*([a-zA-Z0-9_$\\s,]+)\\s*\\}\\s+from\\s+['\"]([a-zA-Z0-9_\\-\\/]+)['\"];";
                            Pattern pattern = Pattern.compile(regex);
                            Matcher matcher = pattern.matcher(fileContent);
                            int lastIndex = 0;
                            while (matcher.find()) {
                                lastIndex = matcher.end();
                            }
                            String importCode = code + "\n";
                            if (lastIndex != 0) {
                                importCode = "\n" + importCode;
                            }
                            document.insertString(lastIndex, importCode);
                        });
                    }
                }
            }
        });
    }

    public static void writeNapi(AnActionEvent e, String rootPath, boolean isNAPI, String libraryName) {
        ApplicationManager.getApplication().invokeLater(() -> {
            if (rootPath != null) {
                String buildProfilePath = rootPath + "/build-profile.json5";
                VirtualFile dependenciesFile = LocalFileSystem.getInstance().findFileByPath(buildProfilePath);
                if (dependenciesFile != null) {
                    Document document = FileDocumentManager.getInstance().getDocument(dependenciesFile);
                    if (document != null) {
                        String content = Json5Parser.parseJson5(document.getText());
                        try {
                            JsonObject jsonObject = JsonParser.parseString(content).getAsJsonObject();
                            if (isNAPI) {
                                if (jsonObject.has("app")) {
                                    JsonObject appObject = jsonObject.get("app").getAsJsonObject();
                                    if (appObject.has("products")) {
                                        JsonArray productsArray = appObject.get("products").getAsJsonArray();
                                        for(int i = 0; i < productsArray.size(); i++) {
                                            JsonObject product = productsArray.get(i).getAsJsonObject();
                                            if (!product.getAsJsonObject().has("buildOption")) {
                                                product.add("buildOption", new JsonObject());
                                            }
                                            JsonObject buildOptionObject = product.get("buildOption").getAsJsonObject();
                                            if (!buildOptionObject.has("externalNativeOptions")) {
                                                buildOptionObject.add("externalNativeOptions", new JsonObject());
                                            }
                                            JsonObject externalNativeOptionsObject = buildOptionObject.get("externalNativeOptions").getAsJsonObject();
                                            if (!externalNativeOptionsObject.has("abiFilters")) {
                                                externalNativeOptionsObject.add("abiFilters", new JsonArray());
                                            }
                                            JsonArray abiArray = externalNativeOptionsObject.get("abiFilters").getAsJsonArray();
                                            if (!abiArray.contains(JsonParser.parseString("arm64-v8a"))) {
                                                abiArray.add("arm64-v8a");
                                            }
                                            if (!abiArray.contains(JsonParser.parseString("X86_64"))) {
                                                abiArray.add("X86_64");
                                            }
                                        }
                                    }
                                }
                                WriteCommandAction.runWriteCommandAction(e.getProject(), ()-> {
                                    document.replaceString(0, document.getTextLength(), jsonObject.toString());
                                    updateLog(libraryName, "build-profile.json5", "", "添加Native配置");
                                    reformatCode(document, Objects.requireNonNull(e.getProject()), dependenciesFile);
                                });
                            }
                        } catch (RuntimeException ex) {
                            throw new RuntimeException(ex);
                        }
                    }
                }
            }
        });
    }

    public static void writeBuildProfile(@NotNull AnActionEvent e, String rootPath, String name, String path, boolean isNAPI, String libraryName) {
        if (name.isEmpty() || path.isEmpty()) {
            return;
        }
        ApplicationManager.getApplication().invokeLater(() -> {
            // 在其他文件中写入代码
            if (rootPath != null) {
                String buildProfilePath = rootPath + "/build-profile.json5";
                VirtualFile dependenciesFile = LocalFileSystem.getInstance().findFileByPath(buildProfilePath);
                if (dependenciesFile != null) {
                    Document document = FileDocumentManager.getInstance().getDocument(dependenciesFile);
                    if (document != null) {
                        String content = Json5Parser.parseJson5(document.getText());
                        try {
                            JsonObject jsonObject = JsonParser.parseString(content).getAsJsonObject();
                            if (isNAPI) {
                                if (jsonObject.has("app")) {
                                    JsonObject appObject = jsonObject.get("app").getAsJsonObject();
                                    if (appObject.has("products")) {
                                        JsonArray productsArray = appObject.get("products").getAsJsonArray();
                                        for (int i = 0; i < productsArray.size(); i++) {
                                            JsonObject product = productsArray.get(i).getAsJsonObject();
                                            if (!product.getAsJsonObject().has("buildOption")) {
                                                product.add("buildOption", new JsonObject());
                                            }
                                            JsonObject buildOptionObject = product.get("buildOption").getAsJsonObject();
                                            if (!buildOptionObject.has("externalNativeOptions")) {
                                                buildOptionObject.add("externalNativeOptions", new JsonObject());
                                            }
                                            JsonObject externalNativeOptionsObject = buildOptionObject.get("externalNativeOptions").getAsJsonObject();
                                            if (!externalNativeOptionsObject.has("abiFilters")) {
                                                externalNativeOptionsObject.add("abiFilters", new JsonArray());
                                            }
                                            JsonArray abiArray = externalNativeOptionsObject.get("abiFilters").getAsJsonArray();
                                            if (!abiArray.contains(JsonParser.parseString("arm64-v8a"))) {
                                                abiArray.add("arm64-v8a");
                                            }
                                            if (!abiArray.contains(JsonParser.parseString("x86_64"))) {
                                                abiArray.add("x86_64");
                                            }
                                        }
                                    }
                                }
                            }
                            if (jsonObject.has("modules")) {
                                JsonArray modulesArray = jsonObject.get("modules").getAsJsonArray();
                                int existPosition = -1;
                                boolean contains = false;
                                for (int i = 0; i < modulesArray.size(); i++) {
                                    if (modulesArray.get(i).getAsJsonObject().has("name")) {
                                        if (modulesArray.get(i).getAsJsonObject().get("name").getAsString().equals(name)) {
                                            contains = true;
                                            existPosition = i;
                                            break;
                                        }
                                    }
                                }
                                if (!contains) {
                                    JsonObject module = new JsonObject();
                                    module.addProperty("name", name);
                                    module.addProperty("srcPath", path);
                                    modulesArray.add(module);
                                } else {
                                    if (existPosition != -1) {
                                        if (!modulesArray.get(existPosition).getAsJsonObject().get("srcPath").getAsString().equals(path)) {
                                            modulesArray.get(existPosition).getAsJsonObject().remove("srcPath");
                                            modulesArray.get(existPosition).getAsJsonObject().addProperty("srcPath", path);
                                        }
                                    }
                                }
                                WriteCommandAction.runWriteCommandAction(e.getProject(), () -> {
                                    document.replaceString(0, document.getTextLength(), jsonObject.toString());
                                    updateLog(libraryName, "build-profile.json5", "", "写入依赖：name：" + name + ", srcPath: " + path);
                                    reformatCode(document, Objects.requireNonNull(e.getProject()), dependenciesFile);
                                    Notification notification = new Notification(Constants.PLUGIN_GROUP_ID, Constants.PLUGIN_NOTIFICATION_TIP, "Add dependencies in " + dependenciesFile.getCanonicalPath(), NotificationType.INFORMATION);
                                    notification.addAction(new NotificationAction(Constants.PLUGIN_NOTIFICATION_OPEN) {
                                        @Override
                                        public void actionPerformed(@NotNull AnActionEvent anActionEvent, @NotNull Notification notification) {
                                            FileEditorManager.getInstance(e.getProject()).openFile(dependenciesFile, true);
                                        }
                                    });
                                    Notifications.Bus.notify(notification, e.getProject());
                                });
                            }
                        } catch (RuntimeException ex) {
                            Notification notification = new Notification(Constants.PLUGIN_GROUP_ID, Constants.PLUGIN_NOTIFICATION_ERROR, ex.getMessage() + "\nPlease check the json format", NotificationType.INFORMATION);
                            notification.addAction(new NotificationAction(Constants.PLUGIN_NOTIFICATION_OPEN) {
                                @Override
                                public void actionPerformed(@NotNull AnActionEvent anActionEvent, @NotNull Notification notification) {
                                    FileEditorManager.getInstance(Objects.requireNonNull(e.getProject())).openFile(dependenciesFile, true);
                                }
                            });
                            Notifications.Bus.notify(notification, e.getProject());
                        }

                    }

                }
            }
        });
    }

    public static void writeDependenciesCode(@NotNull AnActionEvent e, String name, String version, String libraryName) {
        if (name.isEmpty() || version.isEmpty()) {
            return;
        }
        ApplicationManager.getApplication().invokeLater(() -> {
            // 在其他文件中写入代码
            // 这里可以用 VirtualFile 和 PsiFile 的相关API处理文件
            VirtualFile file = e.getData(CommonDataKeys.VIRTUAL_FILE);
            if (file != null) {
                String currentFilePath = file.getCanonicalPath();
                if (currentFilePath != null) {
                    int index = currentFilePath.indexOf("src");
                    if (index != -1) {
                        currentFilePath = currentFilePath.substring(0, index) + "oh-package.json5";
                        VirtualFile dependenciesFile = LocalFileSystem.getInstance().findFileByPath(currentFilePath);
                        if (dependenciesFile != null) {
                            Document document = FileDocumentManager.getInstance().getDocument(dependenciesFile);
                            if (document != null) {
                                String content = Json5Parser.parseJson5(document.getText());
                                try {
                                    JsonObject jsonObject = JsonParser.parseString(content).getAsJsonObject();
                                    if (jsonObject.has("dependencies")) {
                                        JsonObject dependenciesObject = jsonObject.get("dependencies").getAsJsonObject();
                                        dependenciesObject.addProperty(name, version);
                                        jsonObject.add("dependencies", dependenciesObject);
                                        WriteCommandAction.runWriteCommandAction(e.getProject(), () -> {
                                            document.replaceString(0, document.getTextLength(), jsonObject.toString());
                                            InsertUtils.updateLog(libraryName, "oh-package.json5", "", "写入依赖：" + name + ":" + version);
                                            reformatCode(document, Objects.requireNonNull(e.getProject()), dependenciesFile);
                                            Notification notification = new Notification(Constants.PLUGIN_GROUP_ID, Constants.PLUGIN_NOTIFICATION_TIP, "Add dependencies in " + dependenciesFile.getCanonicalPath(), NotificationType.INFORMATION);
                                            notification.addAction(new NotificationAction(Constants.PLUGIN_NOTIFICATION_OPEN) {
                                                @Override
                                                public void actionPerformed(@NotNull AnActionEvent anActionEvent, @NotNull Notification notification) {
                                                    FileEditorManager.getInstance(e.getProject()).openFile(dependenciesFile, true);
                                                }
                                            });
                                            Notifications.Bus.notify(notification, e.getProject());
                                            FileEditorManager.getInstance(e.getProject()).openFile(dependenciesFile, true);
                                        });
                                    }
                                } catch (RuntimeException ex) {
                                    Notification notification = new Notification(Constants.PLUGIN_GROUP_ID, Constants.PLUGIN_NOTIFICATION_ERROR, ex.getMessage() + "\nPlease check the json format", NotificationType.INFORMATION);
                                    notification.addAction(new NotificationAction(Constants.PLUGIN_NOTIFICATION_OPEN) {
                                        @Override
                                        public void actionPerformed(@NotNull AnActionEvent anActionEvent, @NotNull Notification notification) {
                                            FileEditorManager.getInstance(Objects.requireNonNull(e.getProject())).openFile(dependenciesFile, true);
                                        }
                                    });
                                    Notifications.Bus.notify(notification, e.getProject());
                                }

                            }
                        }
                    }
                }
            }
        });
    }

    public static void writeHvigorConfig(@NotNull AnActionEvent e, String rootPath, String name, String version) {
        if (name.isEmpty() || version.isEmpty()) {
            return;
        }
        ApplicationManager.getApplication().invokeLater(() -> {
            // 在其他文件中写入代码
            // 这里可以用 VirtualFile 和 PsiFile 的相关API处理文件
            String hvigorConfig = rootPath + "/" + "hvigor" + "/" + "hvigor-config.json5";
            VirtualFile dependenciesFile = LocalFileSystem.getInstance().findFileByPath(hvigorConfig);
            if (dependenciesFile != null) {
                Document document = FileDocumentManager.getInstance().getDocument(dependenciesFile);
                if (document != null) {
                    String content = Json5Parser.parseJson5(document.getText());
                    try {
                        JsonObject jsonObject = JsonParser.parseString(content).getAsJsonObject();
                        if (jsonObject.has("dependencies")) {
                            JsonObject dependenciesObject = jsonObject.get("dependencies").getAsJsonObject();
                            dependenciesObject.addProperty(name, version);
                            jsonObject.add("dependencies", dependenciesObject);
                            WriteCommandAction.runWriteCommandAction(e.getProject(), () -> {
                                document.replaceString(0, document.getTextLength(), jsonObject.toString());
                                reformatCode(document, Objects.requireNonNull(e.getProject()), dependenciesFile);
                            });
                        }
                    } catch (RuntimeException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        });
    }

    /**
     * 格式化指定文件代码
     *
     * @param document 如果有修改文件中的内容，需要传入修改文件时使用的Document对象，将修改内容提交到文件中，否则会报错。错误内容为Attempt to modify PSI for non-committed Document!
     * @param project  Project对象，可通过AnActionEvent对象获取
     * @param file     需要格式化的文件
     */
    public static synchronized void reformatCode(Document document, @NotNull Project project, VirtualFile file) {
        // 提交document的修改，否则格式化代码时会报错
        if (document != null) {
            commitDocumentChange(document, project);
            PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
            if (psiFile != null) {
                // 格式化指定文件的代码
                CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
                codeStyleManager.reformat(psiFile, false);
            }
        }
    }

    /**
     * 提交文档修改
     *
     * @param document
     * @param project
     */
    private static void commitDocumentChange(Document document, Project project) {
        PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project);
        psiDocumentManager.commitDocument(document);
    }

    /**
     * 通过url获取文件名
     *
     * @param url
     * @return 文件名
     */
    public static @NotNull String getFileNameFromUrl(@NotNull String url) {
        return url.substring(url.lastIndexOf('/') + 1);
    }
    /**
     * 通过url获取文件名
     *
     * @param url
     * @return 文件名
     */
    public static @NotNull String getImageFileNameFromUrl(@NotNull String url) {
        try {
            String fileName = getHash(url);
            return String.format("%s.%s", fileName, url.substring(url.lastIndexOf('.') + 1));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return url.substring(url.lastIndexOf('/') + 1);
    }

    /**
     * 获取缓存路径
     *
     * @return 缓存路径
     */
    public static @NotNull File getCacheDirectory() {
        // 返回合适的缓存目录，例如用户的临时目录
        File file = new File(System.getProperty("java.io.tmpdir"), "Import HarmonyOS Sample Cache");
        if (!file.exists()) {
            file.mkdirs();
        }
        return file;
    }

    /**
     * 复制文件夹
     *
     * @param project
     * @param sourcePath 复制的原文件路径
     * @param targetPath 目标路径
     * @throws IOException
     */
    public static synchronized void copyDirectory(Project project, String sourcePath, String targetPath) throws IOException {
        File sourceDir = new File(sourcePath);
        File targetDir = new File(targetPath);
        // 创建目标目录
        if (!targetDir.exists()) {
            targetDir.mkdirs();
        }
        // 复制文件
        Files.walk(sourceDir.toPath()).forEach(source -> {
            Path target = targetDir.toPath().resolve(sourceDir.toPath().relativize(source));
            try {
                if (Files.isDirectory(source)) {
                    if (!Files.exists(target)) {
                        Files.createDirectory(target);
                    }
                } else {
                    Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

    }

    public static String parseModuleName(AnActionEvent actionEvent, String modulePath) {
        VirtualFile dependenciesFile = LocalFileSystem.getInstance().findFileByPath(modulePath + "/" + "oh-package.json5");
        if (dependenciesFile != null) {
            Document document = FileDocumentManager.getInstance().getDocument(dependenciesFile);
            if (document != null) {
                String content = Json5Parser.parseJson5(document.getText());
                try {
                    JsonObject jsonObject = JsonParser.parseString(content).getAsJsonObject();
                    if (jsonObject.has("name")) {
                        return jsonObject.get("name").getAsString();
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                    return null;
                }
            }
        }
        return null;
    }

    public static int findBuildPositionAboveCaret(Editor editor, Project project) {
        int cursorOffset = editor.getCaretModel().getOffset();
        Document document = editor.getDocument();
        RangeMarker marker = null;
        for(int i = cursorOffset; i >= 0; i--) {
            int lineStart = document.getLineStartOffset(document.getLineNumber(i));
            String lineText = document.getText(new TextRange(lineStart, document.getLineEndOffset(document.getLineNumber(i))));
            if (lineText.startsWith("build() {") || lineText.contains("build(){")) {
                marker = document.createRangeMarker(lineStart, lineStart + lineText.length());
                break;
            }
        }
        if (marker != null) {
            PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
            if (psiFile != null) {
                int insertOffset = marker.getStartOffset();
                return  insertOffset;
            }
        }
        return -1;
    }

    public static int findPositionAboveCaret(Editor editor, Project project) {
        int cursorOffset = editor.getCaretModel().getOffset();
        Document document = editor.getDocument();
        RangeMarker marker = null;
        for(int i = cursorOffset; i >= 0; i--) {
            int lineStart = document.getLineStartOffset(document.getLineNumber(i));
            String lineText = document.getText(new TextRange(lineStart, document.getLineEndOffset(document.getLineNumber(i))));
            if (lineText.startsWith("struct") || lineText.contains("export struct")) {
                marker = document.createRangeMarker(lineStart, lineStart + lineText.length());
                break;
            }
        }
        if (marker != null) {
            PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
            if (psiFile != null) {
                int insertOffset = marker.getStartOffset();
                return  insertOffset;
            }
        }
        return -1;
    }

    public static boolean downloadImageByGit(Project project, String repositoryInfo, String fileName) {
        File cacheDir = new File(getCacheDirectory(), "tmp");
        if (!cacheDir.exists()) {
            cacheDir.mkdirs();
        }
        String repositoryName = getFileNameFromUrl(repositoryInfo).replace(".git", "");
        File repositoryDir = new File(String.format("%s/%s", cacheDir.toPath(), repositoryName));
        if (!repositoryDir.exists()) {
            GitImpl gitImpl = new GitImpl();
            GitLineHandler handler = new GitLineHandler(project, cacheDir, GitCommand.CLONE);
            handler.addParameters(repositoryInfo);
            GitCommandResult result = gitImpl.runCommand(handler);
            int code = result.getExitCode();
            if (code != 0) {
                return false;
            }
        }
        File screenshotsDir = new File(String.format("%s/%s/%s", cacheDir.toPath(), repositoryName, "screenshots"));
        try {
            List<File> findFiles = new ArrayList<>();
            findFileInDir(screenshotsDir, fileName, findFiles);
            if (!findFiles.isEmpty()) {
                File localFile = new File(getCacheDirectory(), fileName);
                Files.copy(findFiles.get(0).toPath(), localFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
                FileUtils.deleteQuietly(repositoryDir);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    private static void findFileInDir(File targetDir, String fileName, List<File> findFiles) {
        if (!findFiles.isEmpty()) {
            return;
        }
        File[] files = targetDir.listFiles();
        if (files == null) {
            return;
        }
        for(File file : files) {
            if (file.isFile() && file.toPath().endsWith(fileName)) {
                findFiles.add(file);
            } else if (file.isDirectory()) {
                findFileInDir(file, fileName, findFiles);
            }
        }
    }

    private static String getHash(String fileUrl) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("md5");
        byte[] hashBytes = md.digest(fileUrl.getBytes());
        StringBuilder sb = new StringBuilder();
        for(byte bte : hashBytes) {
            String hex = Integer.toHexString(0xff & bte);
            if (hex.length() == 1) sb.append('0');
            sb.append(hex);
        }
        return sb.toString();
    }
}
